#include "OpenGLRenderDevice.h"
#include "HerzEngine.h"

namespace Herz
{
	OpenGLRenderDevice::OpenGLRenderDevice(const Resolution& resolution, HWND hWnd) :
		mResolution(resolution),
		mWnd(hWnd)
	{
		initialization();
		setViewport(mResolution.width, mResolution.height);

		mGUI = new hgui::GUI(hgui::hgui_OPENGL);
		mGUI->createWidget<hgui::Button>("Button!", hgui::IntCoord(128,128,128,128), hgui::Color(255,255,0));

		mGUI->initialise();
	}

	OpenGLRenderDevice::~OpenGLRenderDevice()
	{
		delete mGUI;
	}

	void OpenGLRenderDevice::initialization()
	{
		ASSERT(initializeContext(), "'OpenGL' context isn't created!");
		initializeOpenGL();
		checkExtensions();
	}

	bool OpenGLRenderDevice::initializeContext()
	{
		mDC = GetDC(mWnd);

		if (mDC == NULL)
		{
			DestroyWindow(mWnd);
			mWnd = NULL;
			MessageBox(NULL, "mDC is 'null'!", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		PIXELFORMATDESCRIPTOR pfd =	{
			sizeof(PIXELFORMATDESCRIPTOR),
			1,
			PFD_DRAW_TO_WINDOW |
			PFD_SUPPORT_OPENGL |
			PFD_DOUBLEBUFFER,
			PFD_TYPE_RGBA,
			24,
			0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0,
			32,
			0, 0,
			PFD_MAIN_PLANE,
			0, 0, 0, 0				};

		int nPixelFormat = ChoosePixelFormat(mDC, &pfd);

		if (nPixelFormat == NULL)
		{
			ReleaseDC(mWnd, mDC);
			mDC = NULL;
			DestroyWindow(mWnd);
			mWnd = NULL;
			MessageBox(NULL, "Choose pixel format failed!", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		if (SetPixelFormat(mDC, nPixelFormat, &pfd) == false)
		{
			ReleaseDC(mWnd, mDC);
			mDC = NULL;
			DestroyWindow(mWnd);
			mWnd = NULL;
			MessageBox(NULL, "SetPixelFormat failed!", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		mRC = wglCreateContext(mDC);

		if (mRC == NULL)
		{
			ReleaseDC(mWnd, mDC);
			mDC = NULL;
			DestroyWindow(mWnd);
			mWnd = NULL;
			MessageBox(NULL, "m_RC is 'null'","Error", MB_OK | MB_ICONERROR);
			return false;
		}

		if (wglMakeCurrent(mDC, mRC) == false)
		{
			wglDeleteContext(mRC);
			mRC = NULL;
			ReleaseDC(mWnd, mDC);
			mDC = NULL;
			DestroyWindow(mWnd);
			mWnd = NULL;
			MessageBox(NULL, "wglMakeCurrent is failed!", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		ShowWindow(mWnd, SW_NORMAL);

		wglCreateContext(mDC);
		wglMakeCurrent(mDC, mRC);

		ShowCursor(true);

	#ifdef DEBUG_MODE
		std::cout << "Debugging is enabled." << std::endl;
		std::cout << "OpenGL window created: " << mResolution.width << "*" << mResolution.height << " @ " << mResolution.bpp << std::endl;
		std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;
		std::cout << "Herz Engine version: " << VERSION_MAJOR << "." << VERSION_MINOR << "." << VERSION_PATCH << std::endl;
	#endif
		return true;
	}

	void OpenGLRenderDevice::initializeOpenGL()
	{
		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_ALPHA_TEST);
		glShadeModel(GL_SMOOTH);

		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
		glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
		glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
		
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		glEnable(GL_CULL_FACE);
		glCullFace(GL_BACK);
		glFrontFace(GL_CW);
		
		glEnable(GL_TEXTURE_2D);
	}

	void OpenGLRenderDevice::checkExtensions()
	{
		ASSERT(isExtensionSupported("GL_ARB_vertex_buffer_object"), "'GL_ARB_vertex_buffer_object' is NOT supported on your videocard!");
		ASSERT(isExtensionSupported("GL_ARB_vertex_array_object"), "'GL_ARB_vertex_array_object' is NOT supported on your videocard!");
		ASSERT(isExtensionSupported("GL_ARB_multitexture"), "'GL_ARB_multitexture' is NOT suppored on your videocard!");
	}

	void OpenGLRenderDevice::destroy()
	{
		if (mRC)
		{
			if (!wglMakeCurrent(mDC, mRC))
				MessageBox(NULL, "Release of DC and RC failed!", "Error", MB_OK | MB_ICONERROR);

			if (!wglDeleteContext(mRC))
				MessageBox(NULL, "Release rendering context failed!", "Error", MB_OK | MB_ICONERROR);

			mRC = NULL;
		}

		if (mDC && !ReleaseDC(mWnd, mDC))
		{
			MessageBox(NULL, "Release context device failed!", "Error", MB_OK | MB_ICONERROR);
			mDC = NULL;
		}
	}

	void OpenGLRenderDevice::setViewport(int w, int h)
	{
		if (h == 0)
			h = 1;

		glViewport(0, 0, w, h);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();

		gluPerspective(90.0f, (double)w/(double)h, 4.0f, 4096.0f);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	}

	void OpenGLRenderDevice::render()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		begin2D();
		mGUI->update();
		end2D();

		SwapBuffers(mDC);
	}

	void OpenGLRenderDevice::begin2D()
	{
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();

		glOrtho(0, mResolution.width, mResolution.height, 0, 0, 1);

		glMatrixMode(GL_MODELVIEW);

		glLoadIdentity();
		glDisable(GL_DEPTH_TEST);
	}

	void OpenGLRenderDevice::end2D()
	{
		glMatrixMode(GL_PROJECTION);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glEnable(GL_DEPTH_TEST);
	}
} // namespace Herz